unit TVGidsNLParser_Unit;

interface

uses Forms,
     MSHTML,
     Classes,
     SysUtils,
     StrUtils,
     IdHash, IdHashSHA1, IdHashMessageDigest,
     TVGidsNLProgramma_unit,
     TVGidsNLProgrammaLijst_unit;
type
  TTVGidsNLResultaat = (INGELOGD, NIET_INGELOGD, ERROR);

  TTVGidsNLParser = class(TObject)
  private
    { Private declarations }
    function zoekPositieVoorTeken(tekst: String;teken: String;vanafPositie: Integer;aantal: Integer) : Integer;
    function zoekPositieVoorSubStr(tekst: String;subStr: String;vanafPositie: Integer) : Integer;
  public
    { Public declarations }
    function parsAanmeldResultaatNLPagina(pagina: IHTMLDocument2) : TTVGidsNLResultaat;
    function getTVGidsNLGebruikerSHA1(pagina: IHTMLDocument2) : String;
    function isPersoonlijkeVolledigOverzichtGernePagina(pagina: IHTMLDocument2) : Boolean;
    function parsPersoonlijkeVolledigOverzichtGernePagina(pagina: IHTMLDocument2) : TTVGidsNLProgrammaLijst;
  end;

implementation

  {
    * zoekPositieVoorTeken
  }
  function TTVGidsNLParser.zoekPositieVoorTeken(tekst: String;teken: String;vanafPositie: Integer;aantal: Integer) : Integer;
  var
    counter: Integer;
  begin
    result := 0;

    //aantal aftrekken omdat we bij 0 willen stoppen (bij 2 zal de eerste 1 en daarna 0 worden gezien)
    aantal := aantal - 1;

    //tekst doorlopen vanaf een opgegeven positie tot einde van de tekst
    for counter := vanafPositie to (length(tekst) - vanafPositie) do

      //controleren of dit teken het opgegeven teken is
      if tekst[counter] = teken then
      begin

        //controleren of we het aantal van dit teken hebben bereikt
        if aantal = 0 then
        begin

          //teken gevonden
          result := counter;

          //de for counter lus afbreken
          break;
        end
        else
        begin

          //aantal aftellen
          aantal := aantal - 1;
        end;
      end;
  end;


  {
    * zoekPositieVoorTeken
  }
  function TTVGidsNLParser.zoekPositieVoorSubStr(tekst: String;subStr: String;vanafPositie: Integer) : Integer;
  var
    restString: String;
  begin

    //rest in nieuwe string copieren
    if vanafPositie > 0 then
      restString := RightStr(tekst, Length(tekst) - vanafPositie)
    else
      restString := tekst;

     //zoeken in rest string naar subStr
     result := Pos(subStr, restString);

     //controleren of we de subStr hebben gevonden
     if result > 0 then
     begin

       //result ophogen met vanafPositie
       result := result + vanafPositie;
     end;                              
  end;

  {
    * parsAanmeldResultaatNLPagina
  }
  function TTVGidsNLParser.parsAanmeldResultaatNLPagina(pagina: IHTMLDocument2) : TTVGidsNLResultaat;
  //var
    //logFile: TStringList;
  begin


    //html in een file wegschrijven
    //logFile := TStringList.Create();
    //try
    //  logFile.Add(pagina.body.innerHTML);
    //  logFile.SaveToFile(IntToStr(DateTimeToTimeStamp(Now()).Time) + '_htmlLog.txt');
    //finally
    //  logFile.Free();
    //end;


    //standaard is er iets fout gegaan
    result := ERROR;

    //zoeken naar "Ingelogd als" om te controleren of iemand is ingelogd
    if Pos('Uitloggen', pagina.body.innerHTML) > 0 then
    begin

      //gebruiker is ingelogd
      result := INGELOGD;
    end
    else

    //zoeken naar "Inloggen" om te controleren of iemand NIET is ingelogd
    if Pos('Inloggen', pagina.body.innerHTML) > 0 then
    begin

      //gebruiker is niet ingelogd
      result := NIET_INGELOGD;
    end;       
  end;

  {
    * getTVGidsNLGebruikerSHA1
  }
  function TTVGidsNLParser.getTVGidsNLGebruikerSHA1(pagina: IHTMLDocument2) : String;
  var
    hashToSHA1: TIdHashSHA1;
    SHA1Hash: T5x4LongWordRecord;
    counter: Integer;
    hash: String;
    posBegin: Integer;
    posEind: Integer;
    gebruikersNaam: String;

    //functie voor het conveteren van een LongWord naar een String
    function WordToHex(const AValue: LongWord): string;
    var
      value: string;
    begin
      value := IntToHex(AValue, 8);
      result := value[7]+value[8]+value[5]+value[6]+value[3]+value[4]+value[1]+value[2];
    end;

  begin

    //standaard geen hash
    hash := '';

    //standaard geen gebruikersNaam
    gebruikersNaam := 'Onbekend';

    //zoeken naar "Ingelogd als" om te naar het gebruikersnaam te verwijzen
    posBegin := Pos('Ingelogd als', pagina.body.innerHTML);

    //controleren of we de positie hebben gevonden
    if posBegin > 0 then
    begin

      {
        Ingelogd als" src="http://static.tvgids.nl/gfx/s_def/ingelogdals.gif"> <STRONG>Webmug </STRONG>
        pos                                                                  1        2       3
      }

      //doorzoeken naar tweede >
      posBegin := zoekPositieVoorTeken(pagina.body.innerHTML, '>', posBegin + 1, 2);

      //controleren of tweede > is gevonden
      if posBegin > 0 then
      begin

        //zoeken naar eerste < voor eind positie
        posEind := zoekPositieVoorTeken(pagina.body.innerHTML, '<', posBegin + 1, 1);

        //controleren of we eind positie hebben
        if posEind > 0 then
        begin

          //gebruikers naam uitlezen
          gebruikersNaam := MidStr(pagina.body.innerHTML, posBegin + 1, posEind - posBegin - 1);
        end;
      end;
    end;

    //hash object aanmaken
    hashToSHA1 := TIdHashSHA1.Create();
    try
      SHA1Hash := hashToSHA1.HashValue(gebruikersNaam);
      for counter := Low(SHA1Hash) to High(SHA1Hash) do
        hash := hash + WordToHex(SHA1Hash[counter]);
    finally
      FreeAndNil(hashToSHA1);
    end;

    //hash code resulteren
    result := hash;
  end;

  {
    * isPersoonlijkeVolledigOverzichtGernePagina
  }
  function TTVGidsNLParser.isPersoonlijkeVolledigOverzichtGernePagina(pagina: IHTMLDocument2) : Boolean;
  begin
    //standaard niet de juiste pagina
    result := False;

    //controleren op juiste pagina
    if Pos('Dit is jouw persoonlijke gids', pagina.body.innerHTML) > 0 then
      result := True;
  end;

  {
    * parsPersoonlijkeVolledigOverzichtGernePagina
  }
  function TTVGidsNLParser.parsPersoonlijkeVolledigOverzichtGernePagina(pagina: IHTMLDocument2) : TTVGidsNLProgrammaLijst;
  var
    TVGidsNLProgrammaLijst: TTVGidsNLProgrammaLijst;
    TVGidsNLProgramma: TTVGidsNLProgramma;
    posBegin: Integer;
    posEind: Integer;
    posProgrammaBegin: Integer;
    posGenreBegin: Integer;
    posGenreEind: Integer;
    posProgramma: Integer;
    genre: String;
    paginaString: String;
    vorigeBeginTijd: Integer;
    huidigeBeginTijd: Integer;
    huidigeEindTijd: Integer;
  begin

    //TVGidsNL programma lijst aanmaken
    TVGidsNLProgrammaLijst := TTVGidsNLProgrammaLijst.Create();

    {
    <DIV class=programs>
    <DIV class=program id=pers_prog_7692127>
    <A href="/programma/7692127/Science_and_Islam/">
    <SPAN class=time>22:00 - 23:00</SPAN>
    <SPAN class=title>Science and Islam</SPAN>
    <SPAN class=channel>BBC 4</SPAN> </A>
    <SPAN class=options><A onclick="removeProg(7692127,event); return false;" href="#"><IMG alt="Verwijder uit mijn persoonlijke tv-gids" src="http://tvgidsassets.nl/img/btn_remove.gif"></A> </SPAN>
    }

    paginaString := pagina.body.innerHTML;

    //default genre
    genre := 'Onbekend';

    //zoeken naar start programma sectie
    posProgrammaBegin := zoekPositieVoorSubStr(pagina.body.innerHTML, 'class=programs', 0);

    //controleren of we de programma sectie hebben gevonden
    if posProgrammaBegin > 0 then
    begin

      //pagina bijknippen
      paginaString  := RightStr(paginaString, length(paginaString) - posProgrammaBegin);

      //controleren of Genre sectie voor eerste programma komt
      posProgramma := zoekPositieVoorSubStr(paginaString, 'class=program ', 0);

      //controleren of we een programma sectie hebben gevonden
      if posProgramma > 0 then
      repeat

        //zoeken naar start genre sectie (deze komt misschien voor een eerste programma)
        posGenreBegin := zoekPositieVoorSubStr(paginaString, 'class=subtitle', 0);

        //controleren of een genre sectie is gevonden voor een programma
        if posGenreBegin < posProgramma then
        begin

          //zoeken naar genre omschrijving          eerste > tot de eerste <
          posBegin := zoekPositieVoorTeken(paginaString, '>', posGenreBegin + 1, 1);
          posEind  := zoekPositieVoorTeken(paginaString, '<', posBegin + 1, 1)  ;
          genre := MidStr(paginaString, posBegin + 1, posEind - posBegin - 1);
        end;

        //vorige tijd opnieuw instellen voor nieuwe genre (tijd correctie)
        vorigeBeginTijd := 0;

        //repeat

          //controleren of we programma hebben gevonden
          if (posProgramma > 0) then
          begin

            //pagina bijknippen
            paginaString  := RightStr(paginaString, length(paginaString) - posProgramma);

            //nieuwe programma aanmaken
            TVGidsNLProgramma := TTVGidsNLProgramma.Create();

            //programma genre instellen
            TVGidsNLProgramma.var_genre := genre;

            //zoeken naar programma ID                tweede _ tot de eerste >
            posBegin := zoekPositieVoorTeken(paginaString, '_', 0 + 1, 2);
            posEind  := zoekPositieVoorTeken(paginaString, '>', posBegin + 1, 1);
            TVGidsNLProgramma.var_id := MidStr(paginaString, posBegin + 1, posEind - posBegin - 1);

            //zoeken naar programma begintijd         tweede > tot de eerste spatie
            posBegin := zoekPositieVoorTeken(paginaString, '>', posEind + 1, 2);
            posEind  := zoekPositieVoorTeken(paginaString, ' ', posBegin + 1, 1);
            TVGidsNLProgramma.var_begintijd := MidStr(paginaString, posBegin + 1, posEind - posBegin - 1);

            //zoeken naar programma eindtijd          eerste - tot de eerste <
            posBegin := zoekPositieVoorTeken(paginaString, '-', posEind  + 1, 1);
            posEind  := zoekPositieVoorTeken(paginaString, '<', posBegin + 1, 1);
            TVGidsNLProgramma.var_eindtijd := MidStr(paginaString, posBegin + 2, posEind - posBegin - 2); //correctie op eindtijd door spatie

            //zoeken naar programma naam              tweede > tot de eerste <
            posBegin := zoekPositieVoorTeken(paginaString, '>', posEind  + 1, 2);
            posEind  := zoekPositieVoorTeken(paginaString, '<', posBegin + 1, 1);
            TVGidsNLProgramma.var_naam := MidStr(paginaString, posBegin + 1, posEind - posBegin - 1);

            //zoeken naar programma zender            tweede > tot de eerste <
            posBegin := zoekPositieVoorTeken(paginaString, '>', posEind  + 1, 2);
            posEind  := zoekPositieVoorTeken(paginaString, '<', posBegin + 1, 1);
            TVGidsNLProgramma.var_zender := MidStr(paginaString, posBegin + 1, posEind - posBegin - 1);

            ////////////////////////////////////////////////

            //controleren of dagCorrectie noodzakelijk is
            huidigeBeginTijd := StrToIntDef(LeftStr(TVGidsNLProgramma.var_begintijd, 2) + RightStr(TVGidsNLProgramma.var_begintijd, 2) , 0);
            huidigeEindTijd := StrToIntDef(LeftStr(TVGidsNLProgramma.var_eindtijd, 2) + RightStr(TVGidsNLProgramma.var_eindtijd, 2) , 0);

            //controleren of huidige programma eerder is begonnen dan vorige programma
            if huidigeBeginTijd < vorigeBeginTijd then
            begin
              TVGidsNLProgramma.var_beginTijdDagCorrectie := True;
              TVGidsNLProgramma.var_eindTijdDagCorrectie := True;
            end;

            //controleren of eindTijd eerder dan beginTijd is
            if huidigeEindTijd < huidigeBeginTijd then
              TVGidsNLProgramma.var_eindTijdDagCorrectie := True;

            //laatsteTijd onthouden voor volgende programma
            vorigeBeginTijd := huidigeBeginTijd;

            ////////////////////////////////////////////////

            //programma toevoegen aan programma lijst
            TVGidsNLProgrammaLijst.addProgramma(TVGidsNLProgramma);

            //vertraging toevoegen om niet 100% CPU tijd te gebruiken
            Sleep(10);

            //messages processen om TVScanner niet te laten vast lopen
            Application.ProcessMessages();
          end;

          //zoeken naar class=program  (start van table row van een programma)
          posProgramma := zoekPositieVoorSubStr(paginaString, 'class=program ', 0);

          //zoeken naar volgende genre
          //posGenreEind := zoekPositieVoorSubStr(paginaString, 'class=subtitle', 0);

          //controleren of dit de laatste genre is
          //if posGenreEind = 0 then
          //  posGenreEind := length(paginaString);

        //until (posProgramma = 0) or (posProgramma > posGenreEind);

      until (posProgramma = 0);
    end;

    //een tvgidsNLProgramma lijst resulteren
    result := TVGidsNLProgrammaLijst;
  end;

end.
